﻿using JiveMessenger.Converters;
using JiveMessenger.Models;
using JiveMessenger.Utilities;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.ApplicationModel;
using Windows.Foundation;
using Windows.Graphics.Display;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;

namespace JiveMessenger.CustomControls
{
    public partial class ChatControl : UserControl, INotifyPropertyChanged
    {
        private bool _isRotating;
        private static Regex HyperLinkRegex = new Regex(@"(((file|gopher|news|nntp|telnet|http|ftp|https|ftps|sftp)://)|(www\.))+(([a-zA-ZÆæÅåØø0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(/[\w!\&amp;%_\./-~-\(\),\'\[\]+\=#]*)?");
        private readonly CoreDispatcher dispatcher;
        private DateTime _lastScrollAdjustment;
        private DateTime _lastScrolledAwayFromBottom;
        private object LockObject = new object();
        private TappedEventHandler inputTextBoxTappedEventHandler;
        private TappedEventHandler chatcontrolTappedEventHandler;
        //  private bool _lockToBottom;
        public event EventHandler<TappedRoutedEventArgs> OnChatWindowTapped;
        public event EventHandler<TappedRoutedEventArgs> OnInputTextTapped;


        private bool _isLoaded;
        public bool IsLoaded
        {
            get { return _isLoaded; }
            set
            {
                if (_isLoaded != value)
                {
                    _isLoaded = value;
                    RaisePropertyChanged("IsLoaded");
                }
            }
        }



        public bool IsScrolledToEnd
        {
            get { return (bool)GetValue(IsScrolledToEndProperty); }
            set { SetValue(IsScrolledToEndProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsScrolledToEnd.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsScrolledToEndProperty =
            DependencyProperty.Register("IsScrolledToEnd", typeof(bool), typeof(ChatControl), new PropertyMetadata(true));


        public int MaxLinesPerMessage
        {
            get { return (int)GetValue(MaxLinesPerMessageProperty); }
            set { SetValue(MaxLinesPerMessageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxLinesPerMessage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxLinesPerMessageProperty =
            DependencyProperty.Register("MaxLinesPerMessage", typeof(int), typeof(ChatControl), new PropertyMetadata(12));

        public bool IsInputTextFocusedWithoutKeyboard
        {
            get { return (bool)GetValue(IsFocusedWithoutKeyboardProperty); }
            set { SetValue(IsFocusedWithoutKeyboardProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsFocusedWithoutKeyboard.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsFocusedWithoutKeyboardProperty =
            DependencyProperty.Register("IsInputTextFocusedWithoutKeyboard", typeof(bool), typeof(ChatControl), new PropertyMetadata(false, new PropertyChangedCallback(IsInputTextFocusedWithoutKeyboardPropertyCallback)));

        public static readonly DependencyProperty SendMessageCommandProperty = DependencyProperty.Register("SendMessageCommand", typeof(ICommand), typeof(ChatControl), new PropertyMetadata(null));

        public ICommand SendMessageCommand
        {
            get { return (ICommand)GetValue(SendMessageCommandProperty); }
            set { SetValue(SendMessageCommandProperty, value); }
        }

        public Brush InputTextBorderBrush
        {
            get { return (Brush)GetValue(InputTextBorderBrushProperty); }
            set { SetValue(InputTextBorderBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InputTextBorderBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InputTextBorderBrushProperty =
            DependencyProperty.Register("InputTextBorderBrush", typeof(Brush), typeof(ChatControl), new PropertyMetadata(new SolidColorBrush(Colors.Black)));


        public int InputTextSelectionLength
        {
            get { return (int)GetValue(InputTextSelectionLengthProperty); }
            set { SetValue(InputTextSelectionLengthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InputTextSelectionLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InputTextSelectionLengthProperty =
            DependencyProperty.Register("InputTextSelectionLength", typeof(int), typeof(ChatControl), new PropertyMetadata(0));


        public int InputTextSelectionStart
        {
            get { return (int)GetValue(InputTextSelectionStartProperty); }
            set { SetValue(InputTextSelectionStartProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InputTextSelectionStart.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InputTextSelectionStartProperty =
            DependencyProperty.Register("InputTextSelectionStart", typeof(int), typeof(ChatControl), new PropertyMetadata(0));

        public string InputText
        {
            get { return (string)GetValue(InputTextProperty); }
            set { SetValue(InputTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InputText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InputTextProperty =
            DependencyProperty.Register("InputText", typeof(string), typeof(ChatControl), new PropertyMetadata(string.Empty));



        public bool IsInputTextEnabled
        {
            get { return (bool)GetValue(IsInputTextEnabledProperty); }
            set { SetValue(IsInputTextEnabledProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsInputTextEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsInputTextEnabledProperty =
            DependencyProperty.Register("IsInputTextEnabled", typeof(bool), typeof(ChatControl), new PropertyMetadata(true));



        public bool IsInputTextBlockFocused
        {
            get { return (bool)GetValue(IsInputTextBlockFocusedProperty); }
            set { SetValue(IsInputTextBlockFocusedProperty, value); }
        }
        // Using a DependencyProperty as the backing store for IsInputTextBlockFocused.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsInputTextBlockFocusedProperty =
            DependencyProperty.Register("IsInputTextBlockFocused", typeof(bool), typeof(ChatControl), new PropertyMetadata(false, new PropertyChangedCallback(IsInputTextBlockFocusedProperty_Callback)));

        public RangeObservableCollection<MessageModel> Messages
        {
            get { return (RangeObservableCollection<MessageModel>)GetValue(MessagesProperty); }
            set { SetValue(MessagesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Messages.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MessagesProperty =
            DependencyProperty.Register("Messages", typeof(RangeObservableCollection<MessageModel>), typeof(ChatControl), new PropertyMetadata(null, new PropertyChangedCallback(Messages_Callback)));



        public double InputTextControlOpacity
        {
            get { return (double)GetValue(InputTextControlOpacityProperty); }
            set { SetValue(InputTextControlOpacityProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InputTextControlOpacity.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InputTextControlOpacityProperty =
            DependencyProperty.Register("InputTextControlOpacity", typeof(double), typeof(ChatControl), new PropertyMetadata(1.0));



        public double VerticalScrollOffset
        {
            get { return (double)GetValue(VerticalScrollOffsetProperty); }
            set { SetValue(VerticalScrollOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for VerticalScrollOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty VerticalScrollOffsetProperty =
            DependencyProperty.Register("VerticalScrollOffset", typeof(double), typeof(ChatControl), new PropertyMetadata(0));

        ~ChatControl()
        {
            Debug.WriteLine("ChatControl with ID {0} destructed", this.GetHashCode());

        }

        public ChatControl()
        {
            InitializeComponent();
            if (DesignMode.DesignModeEnabled)
            {
                return;
            }
            this.LayoutRoot.DataContext = this;
            inputTextBoxTappedEventHandler = new TappedEventHandler(InputTextBox_Tapped);
            chatcontrolTappedEventHandler = new TappedEventHandler(ChatControl_Tapped);
            //var scrollbars = scrollViewer.GetDescendantsOfType<ScrollBar>().ToList();
            //var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical);
            //if (verticalBar != null)
            //    verticalBar.Scroll += BarScroll;
            this.dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;

        }

        private async void OrientationChanged(DisplayInformation sender, object args)
        {
            bool forceScroll = IsScrolledToEnd;
            _isRotating = true;
            await Task.Delay(300);
            _isRotating = false;
            SetScrollBar(forceScroll);
        }

        /// <summary>
        /// Fade in chat log listview over the given duration
        /// </summary>
        /// <param name="duration">Duration in milliseconds</param>
        public void FadeInChatLog(int duration, int startTime = 0)
        {
            this.ChatBalloonList.Animate(0, 1, "Opacity", duration, startTime);
        }
        /// <summary>
        /// Fade out chat log listview over the given duration
        /// </summary>
        /// <param name="duration">Duration in milliseconds</param>
        public void FadeOutChatLog(int duration, int startTime = 0)
        {
            this.ChatBalloonList.Opacity = 0;
        }

        private async Task OnUiThread(Action action)
        {
            await this.dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action());
        }
        private async Task OnUiThread(Func<Task> action)
        {
            await this.dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => await action());
        }


        private static void IsInputTextFocusedWithoutKeyboardPropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ChatControl cc = d as ChatControl;

            if ((bool)e.NewValue == true)
                cc.InputTextBox.Focus(FocusState.Programmatic);
        }


        void scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
        {
            if (e.IsIntermediate)
                return;
            ScrollViewer scrollViewer = (ScrollViewer)sender;
            var scrollbars = scrollViewer.GetDescendantsOfType<ScrollBar>().ToList();
            var bar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical);
            if (bar != null)
                if (bar == null)
                    return;
            VerticalScrollOffset = scrollViewer.VerticalOffset;

            System.Diagnostics.Debug.WriteLine("Scrolling ended");

            if (scrollViewer.VerticalOffset >= bar.Maximum)
            {
                System.Diagnostics.Debug.WriteLine("We are at the bottom");
                IsScrolledToEnd = true;
                StopScrollBlink();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("We are away from the bottom");
                IsScrolledToEnd = false;
                _lastScrolledAwayFromBottom = DateTime.Now;
            }
        }

        //private void BarScroll(object sender, ScrollEventArgs e)
        //{
        //    if (e.ScrollEventType != ScrollEventType.EndScroll) return;

        //    var bar = sender as ScrollBar;
        //    if (bar == null)
        //        return;

        //    System.Diagnostics.Debug.WriteLine("Scrolling ended");

        //    if (e.NewValue >= bar.Maximum)
        //    {
        //        System.Diagnostics.Debug.WriteLine("We are at the bottom");
        //        _lockToBottom = true;
        //    }
        //    else
        //    {
        //        System.Diagnostics.Debug.WriteLine("We are away from the bottom");
        //        _lockToBottom = false;
        //    } 
        //}
        private static void Messages_Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (DesignMode.DesignModeEnabled)
                return;

            ChatControl cc = d as ChatControl;
            if (e.OldValue != null)
            {
                RangeObservableCollection<MessageModel> messages = e.OldValue as RangeObservableCollection<MessageModel>;
                if (messages != null)
                {
                    messages.CollectionChanged -= cc.Messages_CollectionChanged;
                }
            }
            if (e.NewValue != null)
            {
                RangeObservableCollection<MessageModel> messages = e.NewValue as RangeObservableCollection<MessageModel>;
                if (messages != null)
                {
                    messages.CollectionChanged += cc.Messages_CollectionChanged;
                    if (messages.Count > 1)
                    {
                        //todo:fix scrolling
                        //Thread t = new Thread(() =>
                        //{
                        //    Thread.Sleep(500);
                        //    cc.Dispatcher.BeginInvoke(() =>
                        //    {
                        //cc.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                        //    {
                        cc.SetScrollBar(true);

                        //});
                        //    });
                        //});
                        //t.Start();
                    }
                }
            }
        }

        private static void IsInputTextBlockFocusedProperty_Callback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            ChatControl cc = dependencyObject as ChatControl;
            if ((bool)dependencyPropertyChangedEventArgs.NewValue == true)
            {
                cc.InputTextBox.Focus(FocusState.Pointer);
            }
            else
            {
                cc.Focus(FocusState.Programmatic);
            }
        }

        void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                bool forceScroll = false;
                foreach (MessageModel m in e.NewItems)
                {
                    if (m.IsLastInBatch && e.NewItems.Count > 1)
                    {
                        forceScroll = true;
                        break;
                    }
                }

                if (forceScroll)
                {
                    //todo:fix scrolling
                    //Thread.Sleep(500);
                    //Debug.WriteLine("Last queued message received, setting scrollbar in 500ms");
                }
                SetScrollBar(forceScroll);
            }
        }



        //private List<MessageModel> GetVisibleItems(ListView lls)
        //{
        //    var offset = this._viewPortControl.Viewport.Height;
        //    List<MessageModel> visibleItems = new List<MessageModel>();
        //    double totalHeight = 0;
        //    this.items = this.items.OrderBy(x => Canvas.GetTop(x.Value)).ToDictionary(x => x.Key, x => x.Value);
        //    foreach (KeyValuePair<MessageModel, ContentPresenter> kvp in this.items)
        //    {
        //        if ((totalHeight + kvp.Value.ActualHeight) < offset)
        //        {
        //            visibleItems.Add((MessageModel)kvp.Key);
        //            totalHeight += kvp.Value.ActualHeight;
        //        }
        //        else
        //            break;
        //    }
        //    return visibleItems;
        //    //return items.Where(x => Canvas.GetTop(x.Value) + x.Value.ActualHeight > offset)
        //    //    .OrderBy(x => Canvas.GetTop(x.Value)).ToDictionary(x=>x.Key,x=>x.Value).Keys.ToList();
        //}
        //private static ViewportControl FindViewport(DependencyObject parent)
        //{
        //    var childCount = VisualTreeHelper.GetChildrenCount(parent);
        //    for (var i = 0; i < childCount; i++)
        //    {
        //        var elt = VisualTreeHelper.GetChild(parent, i);
        //        if (elt is ViewportControl) return (ViewportControl)elt;
        //        var result = FindViewport(elt);
        //        if (result != null) return result;
        //    }
        //    return null;
        //}

        private static ScrollBar FindScrollbar(DependencyObject parent)
        {
            var childCount = VisualTreeHelper.GetChildrenCount(parent);
            for (var i = 0; i < childCount; i++)
            {
                var elt = VisualTreeHelper.GetChild(parent, i);
                if (elt is ScrollBar) return (ScrollBar)elt;
                var result = FindScrollbar(elt);
                if (result != null) return result;
            }
            return null;
        }
        private static Thumb FindThumb(DependencyObject parent)
        {
            var childCount = VisualTreeHelper.GetChildrenCount(parent);
            for (var i = 0; i < childCount; i++)
            {
                var elt = VisualTreeHelper.GetChild(parent, i);
                if (elt is Thumb) return (Thumb)elt;
                var result = FindThumb(elt);
                if (result != null) return result;
            }
            return null;
        }



        public void SetScrollBar(bool forceScrollToEnd = false, double scrollOffset = -1, bool animate = true)
        {
            if (Messages == null || Messages.Count == 0 || DesignMode.DesignModeEnabled)
                return;

            bool isScrollbarVisible = false;//todo: find a way to check if scrollbar is visible
            var scrollViewer = this.ChatBalloonList.GetFirstDescendantOfType<ScrollViewer>();
            if (scrollViewer == null)
                return;
            if (scrollViewer.IsInVisualTree())
            {
                var bounds = scrollViewer.GetBoundingRect();
                isScrollbarVisible = scrollViewer.ExtentHeight > bounds.Height;//(Math.Round(bounds.Height) >Math.Round(scrollViewer.ViewportHeight));
            }

            if (IsScrolledToEnd || forceScrollToEnd)
            {
                Task.Run(async () =>
                    {
                        await OnUiThread(() =>
                         {
                             scrollViewer.ChangeView(null, scrollViewer.ExtentHeight, null, !animate);
                             StopScrollBlink();
                             Debug.WriteLine("changed scrollviewer view");
                         });
                    });
            }
            else if (scrollOffset >= 0)
            {
                Task.Run(async () =>
                {
                    await OnUiThread(() =>
                    {
                        scrollViewer.ChangeView(null, scrollOffset, null);
                        StopScrollBlink();
                    });
                });
            }
            else if (isScrollbarVisible)
            {
                StartScrollBlink();
                //if (this._blinkScrollbarThread == null)
                //{
                //    ThreadStart t = delegate
                //        {
                //            this._isScrollbarBlinking = true;
                //            bool blinked = false;
                //            int lastBlinkMs = 200;
                //            while (true)
                //            {
                //                if (!this._isScrollbarBlinking)
                //                {
                //                    this.Dispatcher.BeginInvoke(() =>
                //                    {
                //                        this.BlinkGrid.Background = new SolidColorBrush(Colors.Transparent);
                //                    });
                //                    this._blinkScrollbarThread = null;
                //                    break;
                //                }
                //                Thread.Sleep(10);

                //                if (lastBlinkMs >= 200)
                //                {
                //                    this.Dispatcher.BeginInvoke(() =>
                //                    {
                //                        this.BlinkGrid.Background = blinked ? new SolidColorBrush(Colors.White) : new SolidColorBrush(Colors.Transparent);
                //                        blinked = !blinked;
                //                    });
                //                    lastBlinkMs = 0;
                //                }
                //                lastBlinkMs += 10;
                //            }

                //        };
                //    this._blinkScrollbarThread = new Thread(t) { IsBackground = true, Name = "BlinkScrollbarThread" };
                //    this._blinkScrollbarThread.Start();
                //}
            }
        }
        private void StartScrollBlink()
        {
            Storyboard sb = Resources["StartBlinkGridAnim"] as Storyboard;
            if (sb.GetCurrentState() != ClockState.Active)
                sb.Begin();
        }

        private void StopScrollBlink()
        {
            Storyboard sb = Resources["StartBlinkGridAnim"] as Storyboard;
            sb.Stop();
        }

        private void InputTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            if (!IsInputTextBlockFocused)// && !IsInputTextFocusedWithoutKeyboard)
                IsInputTextBlockFocused = true;

        }

        private void InputTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            if (IsInputTextBlockFocused)
                IsInputTextBlockFocused = false;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            IsLoaded = true;
            if (DesignMode.DesignModeEnabled)
                return;
            var scrollViewer = this.ChatBalloonList.GetFirstDescendantOfType<ScrollViewer>();
            scrollViewer.ViewChanged += scrollViewer_ViewChanged;
            DisplayInformation.GetForCurrentView().OrientationChanged += OrientationChanged;
            this.InputTextBox.AddHandler(TappedEvent, inputTextBoxTappedEventHandler, true);
            this.ChatBalloonList.AddHandler(TappedEvent, chatcontrolTappedEventHandler, true);
            Debug.WriteLine("ChatControl with ID {0} loaded", this.GetHashCode());

            //   Messages.CollectionChanged += Messages_CollectionChanged;

            //#if WINDOWS_PHONE_APP
            //            var applicationView = ApplicationView.GetForCurrentView();
            //            applicationView.VisibleBoundsChanged += ChatControl_VisibleBoundsChanged;
            //            this.VisibleBoundsHeight = applicationView.VisibleBounds.Height;
            //            this.VisibleBoundsWidth = applicationView.VisibleBounds.Width;
            //#else
            //            this.SizeChanged += ChatControl_SizeChanged;
            //            this.VisibleBoundsHeight = this.ActualHeight;
            //            this.VisibleBoundsWidth = this.ActualWidth;
            //#endif
            //var root = Window.Current.Content as Frame;
            //root.RenderTransform = new CompositeTransform() { TranslateY = 0 };

            // WP8PullDetector detector = new WP8PullDetector();
            //detector.Bind(this.ChatBalloonList);
            //detector.Compression += detector_Compression;
            //items = new Dictionary<MessageModel, ContentPresenter>();
            //var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
            //rootFrame.RenderTransform = new CompositeTransform() { TranslateY = +0 };

            //var scrollBar = FindScrollbar(this.ChatBalloonList);
            //var thumb = FindThumb(this.ChatBalloonList);
            //thumb.DragStarted += thumb_DragStarted;
            //thumb.DragDelta += thumb_DragDelta;
            //thumb.DragCompleted += thumb_DragCompleted;



            //this.ChatControlWidth = this.ActualWidth;
        }
        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            IsLoaded = false;
            var scrollViewer = this.ChatBalloonList.GetFirstDescendantOfType<ScrollViewer>();
            scrollViewer.ViewChanged -= scrollViewer_ViewChanged;
            DisplayInformation.GetForCurrentView().OrientationChanged -= OrientationChanged;
            this.InputTextBox.RemoveHandler(TappedEvent, inputTextBoxTappedEventHandler);
            this.ChatBalloonList.RemoveHandler(TappedEvent, chatcontrolTappedEventHandler);
            Messages.CollectionChanged -= Messages_CollectionChanged;

            Debug.WriteLine("ChatControl with ID {0} unloaded", this.GetHashCode());
            //#if WINDOWS_PHONE_APP
            //            ApplicationView.GetForCurrentView().VisibleBoundsChanged -= ChatControl_VisibleBoundsChanged;
            //#else
            //            this.SizeChanged -= ChatControl_SizeChanged;
            //#endif
        }
        //#if WINDOWS_APP
        //        void ChatControl_SizeChanged(object sender, SizeChangedEventArgs e)
        //        {
        //            this.VisibleBoundsWidth = this.ActualWidth;
        //            this.VisibleBoundsHeight = this.ActualHeight;
        //        }
        //#endif
        //void detector_Compression(object sender, CompressionEventArgs e)
        //{
        //    if (e.Type == CompressionType.Bottom)
        //    {
        //        _isScrollbarBlinking = false;
        //    }
        //}
        //#if WINDOWS_PHONE_APP
        //        void ChatControl_VisibleBoundsChanged(ApplicationView sender, object args)
        //        {
        //            this.VisibleBoundsHeight = sender.VisibleBounds.Height;
        //            this.VisibleBoundsWidth = sender.VisibleBounds.Width;
        //        }
        //#endif
        private void ChatBalloonList_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //lock (LockObject)
            //{

            //this.ChatControlWidth = e.NewSize.Width;
            if (DateTime.Now.Subtract(_lastScrollAdjustment).TotalMilliseconds < 170)
                return;
            if (e.NewSize.Height < e.PreviousSize.Height)
            {
                _lastScrollAdjustment = DateTime.Now;
                //Thread t = new Thread(() =>
                //    {
                //        Thread.Sleep(170);
                //  await Task.Delay(170);
                //this.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                // {
                SetScrollBar(true);
                //});
                //    });
                //t.Start();
            }
            //}
        }

        //private void ChatBalloonList_Tapped(object sender, TappedRoutedEventArgs e)
        //{
        //    if (OnChatWindowTapped != null)
        //        OnChatWindowTapped(this, e);
        //}

        private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            if (e.Key == Windows.System.VirtualKey.Enter && !String.IsNullOrEmpty(textBox.Text))
            {
                SendMessageCommand.Execute(textBox.Text);
                //textBox.Text = string.Empty;
            }
        }

        private void InputTextBox_Tapped(object sender, TappedRoutedEventArgs e)
        {
            if (OnInputTextTapped != null)
                OnInputTextTapped(this, e);
        }

        private void bnExpandMessage_Click(object sender, RoutedEventArgs e)
        {
            Button bn = sender as Button;
            if (bn.Tag != null)
            {
                MessageModel model = bn.Tag as MessageModel;
                if (model != null)
                {
                    this.ExpandedMessageGrid.DataContext = model;
                    // this.ExpandedMessagePopup.Width = this.ExpandedMessagePopupGrid.Width = this.VisibleBoundsWidth;
                    // this.ExpandedMessagePopup.Height = this.ExpandedMessagePopupGrid.Height = this.VisibleBoundsHeight;
                    //  this.ExpandedMessagePopup.IsOpen = true;
                    Storyboard sb = this.Resources["ShowOverflowGridAnim"] as Storyboard;
                    sb.Begin();
                }
            }
        }

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

        private void RichTextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //if (DateTime.Now.Subtract(_lastScrollAdjustment).TotalMilliseconds < 170)
            //    return;
            // await Task.Delay(20);
            RichTextBlock rtb = (RichTextBlock)sender;
            if (e.NewSize.Equals(e.PreviousSize))
                return;
            //if (ApplicationView.GetForCurrentView().Orientation == ApplicationViewOrientation.Landscape && rtb.MaxLines!=8)
            //    rtb.MaxLines = 8;
            //else if(rtb.MaxLines!=14)
            //    rtb.MaxLines = 14;
            //  Debug.WriteLine(rtb.HasOverflowContent);
            MessageModel model = rtb.DataContext as MessageModel;

            bool forceScroll = model == this.Messages.LastOrDefault();
            if (forceScroll && (IsScrolledToEnd))// || DateTime.Now.Subtract(_lastScrolledAwayFromBottom).TotalMilliseconds < 500))
            {
                SetScrollBar(forceScroll);
                Debug.WriteLine(DateTime.Now + " : scrolling " + " forcescroll is " + forceScroll);
            }
            else
            {
                // Debug.WriteLine(DateTime.Now + " : not scrolling, IsScrolledToEnd is" + IsScrolledToEnd + " forcescroll is " + forceScroll);
            }
            _lastScrollAdjustment = DateTime.Now;
        }

        private void ChatBalloonList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            //    return;

            MessageModel model = args.Item as MessageModel;
            Grid templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
            Image incomingAvatar = (Image)templateRoot.FindName("IncomingAvatarImg");
            Image outgoingAvatar = (Image)templateRoot.FindName("OutgoingAvatarImg");
            Button expandMessageButton = (Button)templateRoot.FindName("bnExpandMessage");
            Border ChatbubbleBorder = (Border)templateRoot.FindName("ChatbubbleBorder");
            Path OutgoingUserSpikePath = (Path)templateRoot.FindName("OutgoingUserSpikePath");
            Path IncomingUserSpikePath = (Path)templateRoot.FindName("IncomingUserSpikePath");
            TextBlock TextBlockUsername = (TextBlock)templateRoot.FindName("TextBlockUsername");
            TextBlock TextBlockMessageDate = (TextBlock)templateRoot.FindName("TextBlockMessageDate");
            RichTextBlock RtbChatText = (RichTextBlock)templateRoot.FindName("RtbChatText");
            if (args.InRecycleQueue)
            {
                // TextBlockUsername.ClearValue(TextBlock.TextProperty);
                TextBlockMessageDate.ClearValue(TextBlock.TextProperty);
                incomingAvatar.ClearValue(Image.SourceProperty);
                outgoingAvatar.ClearValue(Image.SourceProperty);
                outgoingAvatar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                incomingAvatar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                RtbChatText.Blocks.Clear();
                RtbChatText.DataContext = null;
                return;
            }
            if (args.Phase == 0)
            {
                templateRoot.DataContext = model;
                RtbChatText.DataContext = model;
                //  RtbChatText.Opacity = 0;
                TextBlockMessageDate.Opacity = 0;
                IncomingUserSpikePath.Opacity = 0;
                OutgoingUserSpikePath.Opacity = 0;
                TextBlockUsername.Opacity = 0;
                //TextBlockUsername.FontWeight = FontWeights.Normal;
                //TextBlockUsername.FontSize = 14;
                incomingAvatar.Opacity = 0;
                outgoingAvatar.Opacity = 0;
                //expandMessageButton.Visibility = (Visibility)new MessageTextToExpanderVisibilityConverter().Convert(model.MessageText, typeof(string), null, null);
                expandMessageButton.Opacity = 1;
                // expandMessageButton.Tag = model;
                //Canvas.SetZIndex(ChatbubbleBorder, 1);
                ChatbubbleBorder.Opacity = 1;
                //   ChatbubbleBorder.Animate(0, 1, "Opacity", 1000, 0);
                //   FontColorToUserColorConverter fontColorConverter = new FontColorToUserColorConverter();
                // RichTextBlockExtension.SetSplittedMessageItems(RtbChatText, model.MessageItems);
                //  RtbChatText.Foreground = model.Sender.FontColor;
                //  RtbChatText.Opacity = 1;
                //ChatbubbleBorder.BorderBrush = (SolidColorBrush)fontColorConverter.Convert(model.Sender.FontColor, typeof(SolidColorBrush), null, null);
                if (model.Type == MessageModelType.Incoming)
                {
                    //IncomingUserSpikePath.Animate(0, 1, "Opacity", 1000, 0);
                    //  Canvas.SetZIndex(IncomingUserSpikePath, 1);
                    // IncomingUserSpikePath.Fill = (SolidColorBrush)fontColorConverter.Convert(model.Sender.FontColor, typeof(SolidColorBrush), null, null);
                    IncomingUserSpikePath.Opacity = 1;
                }
                //  IncomingUserSpikePath.Opacity = 1;
                else if (model.Type == MessageModelType.Outgoing)
                {
                    // OutgoingUserSpikePath.Animate(0, 1, "Opacity", 1000, 0);
                    //Canvas.SetZIndex(OutgoingUserSpikePath, 1);
                    // OutgoingUserSpikePath.Fill = (SolidColorBrush)fontColorConverter.Convert(model.Sender.FontColor, typeof(SolidColorBrush), null, null);
                    OutgoingUserSpikePath.Opacity = 1;
                }
                // OutgoingUserSpikePath.Opacity = 1;
                args.RegisterUpdateCallback(ContainerContentChangingDelegate);
            }
            else if (args.Phase == 1)
            {
                if (model.IsUserNameVisible)
                {
                    // TextBlockUsername.Text = model.Sender.PrettyName;
                    TextBlockUsername.Opacity = 1;
                    //TextBlockUsername.Animate(0, 1, "Opacity", 1000, 0);
                }
                TextBlockMessageDate.Text = (string)new DateTimeToShortTimeStringConverter().Convert(model.TimeStamp, typeof(string), null, null);
                TextBlockMessageDate.Opacity = 1;
                // TextBlockMessageDate.Animate(0, 1, "Opacity", 1000, 0);
                // expandMessageButton.Animate(0, 1, "Opacity", 1000, 0);
                args.RegisterUpdateCallback(ContainerContentChangingDelegate);
            }
            //else if (args.Phase == 2)
            //{
            //    RtbChatText.Opacity = 1;a
            //    args.RegisterUpdateCallback(ContainerContentChangingDelegate);
            //}
            else if (args.Phase == 2)
            {
                if (model.Type == MessageModelType.Incoming)
                {
                    Canvas.SetZIndex(incomingAvatar, -1);
                    incomingAvatar.Source = new BitmapImage(new Uri(model.Sender.Avatar));
                    incomingAvatar.Visibility = Windows.UI.Xaml.Visibility.Visible;
                    incomingAvatar.Animate(0, 1, "Opacity", 200, 0);
                    //  incomingAvatar.Opacity = 1;
                }
                else if (model.Type == MessageModelType.Outgoing)
                {
                    Canvas.SetZIndex(outgoingAvatar, -1);

                    outgoingAvatar.Source = new BitmapImage(new Uri(model.Sender.Avatar));// model.Sender.Avatar;
                    outgoingAvatar.Visibility = Windows.UI.Xaml.Visibility.Visible;
                    outgoingAvatar.Animate(0, 1, "Opacity", 200, 0);

                    //  outgoingAvatar.Opacity = 1;

                }
            }
            args.Handled = true;
        }

        private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> ContainerContentChangingDelegate
        {

            get
            {

                if (_delegate == null)
                {

                    _delegate = new TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs>(ChatBalloonList_ContainerContentChanging);

                }

                return _delegate;

            }

        }

        private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> _delegate;

        private void ExpandedMessagePopupGrid_Tapped(object sender, TappedRoutedEventArgs e)
        {
            HideMessageOverflow();
        }

        private void CloseExpandedMessageButton_Tapped(object sender, TappedRoutedEventArgs e)
        {
            HideMessageOverflow();
        }
        private void HideMessageOverflow()
        {
            Storyboard sb = this.Resources["HideOverflowGridAnim"] as Storyboard;
            sb.Begin();
        }

        private void MessageItem_PointerExited(object sender, PointerRoutedEventArgs e)
        {
            //if (OnChatWindowTapped != null)
            //    OnChatWindowTapped(this, null);
        }

        private void ChatControl_Tapped(object sender, TappedRoutedEventArgs e)
        {
            if (OnChatWindowTapped != null)
                OnChatWindowTapped(this, e);
        }
    }
}
